/*
 * Created by: 
 * Created: Wednesday, February 21, 2007
 */

using System;
using System.Collections.Generic;
using System.Reflection;
using Edge.Business.Domain;
using Edge.Business.Entities;
using Edge.Business.Managers;
using Edge.Common.Services;
using Edge.Common.Utils;
using NHibernate;

namespace Edge.DAL.Managers
{
    public class EntityLoader : IEntityLoader, IEntitySaver
    {
        private Dictionary<Type, Type> entityManager = new Dictionary<Type, Type>(200);

        public EntityLoader()
        {
            ScanTypes();
        }

        private void ScanTypes()
        {
            Type[] domainManagersTypes = Assembly.GetAssembly(typeof(IEntityLoader)).GetTypes();
            foreach (Type type in domainManagersTypes)
            {
                if (!type.IsInterface)
                    continue;

                Type[] interfaces = type.GetInterfaces();
                foreach (Type interfaceType in interfaces)
                {
                    if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IAbstractManager<,>))
                    {
                        Type entityType = interfaceType.GetGenericArguments()[0];
                        FoundManager(entityType, type);
                    }
                }
            }
        }


        private void FoundManager(Type entityType, Type managerType)
        {
            if (!entityManager.ContainsKey(entityType))
            {
                entityManager[entityType] = managerType;
            }
            else
            {
                if (entityManager[entityType].IsAssignableFrom(managerType))
                    entityManager[entityType] = managerType;
            }
        }

        public object Load(Type type, object id)
        {
            if (type == null)
                throw new ArgumentException("type");
            if (id == null)
                throw new ArgumentException("id");

            object manager = GetManager(type);

            return ReflectionUtils.Invoke(manager, "Load", id);
        }

        protected object GetManager(Type entityType)
        {
            Type managerType = null;

            if (entityManager.ContainsKey(entityType))
                managerType = entityManager[entityType];
            else
            {
                foreach (KeyValuePair<Type, Type> pair in entityManager)
                {
                    if (pair.Key.IsAssignableFrom(entityType))
                    {
                        managerType = pair.Value;
                        break;
                    }
                }
            }

            if (managerType == null)
                throw new ArgumentException("could not load object type of " + entityType);

            object manager = Locator.GetService(managerType);
            if (manager == null)
                throw new InvalidOperationException("locator does not containsservice type of " + managerType);
            return manager;
        }

        public T Load<T, IT>(IT id)
        {
            return (T)Load(typeof(T), id);
        }

        /// <summary>
        /// load entity by id
        /// retunr null if object does not exists
        /// </summary>
        /// <param name="type"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public object Get(Type type, object id)
        {
            try
            {
                return Load(type, id);
            }
            catch (ObjectNotFoundException ex)
            {
                return null;
            }

        }


        /// <summary>
        /// load entity by id
        /// retunr null if object does not exists
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="IT"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public T Get<T, IT>(IT id)
        {
            return (T)this.Get(typeof(T), id);
        }


        public void Save(IEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            Type type = entity.GetType();
            // TODO check for proxies
            object manager = GetManager(type);

            ReflectionUtils.Invoke(manager, "Save", entity);
        }
    }
}